/*
 * :Author: 沈玲玲
 * :Company: 蓝萌电子
 * :Date: 2023-06-01 02:48:16
 * :LastEditTime: 2023-06-01 05:07:27
 * :LastEditors: 沈玲玲
 * :FilePath: \TiantuBUSDemo\lib\TiantuBus.c
 * :Version: 1.0.0
 * :Language: C/C++
 * :Coding: UTF-8
 * :Copyright: 2023 蓝萌电子.
 * :All Rights Reserved.
 * :This software component is licensed under GPL-3.0.
 * :You may not use this file except in compliance with the License.
 * :Description: 蓝萌电子专属数据协议：TiantuBus
 * 可以适用于单片机与上位机，单片机与单片机之间的通信，支持IIC,SPI,UART等
 * 通信协议，支持STM32,51,AVR,STM8等多种类型的单片机，只要是蓝萌电子开发
 * 的设备，在知道机器编号的情况下，便可以直接使用通信协议，并拥有专属调试的
 * 串口助手。
 * 
 * 格式：
 * 1.发送命令（控制寄存器）
 * 前导码（2字节） +  目标设备编号（2字节） + 操作类型（1字节）
 * + 命令（1字节）+ 寄存器地址（1字节）+ 校验（1字节）+ 包尾（2字节）
 * 总长度：10个字节
 * 返回：应答拓展指令
 * 
 * 2.发送数据（设置储存变量的值）
 * 前导码（2字节） +  目标设备编号（2字节） + 操作类型（1字节）
 * + 数据长度（2字节）+ 数据包内容（N字节）+ 存储器地址（2字节）
 * + 校验（1字节）+ 包尾（2字节）
 * 总长度：12 + N个字节
 * 返回：应答拓展指令
 * 
 * 3.访问寄存器（读取寄存器）
 * 前导码（2字节） +  目标设备编号（2字节） + 操作类型（1字节）
 * + 寄存器地址（1字节） + 校验（1字节）+ 包尾（2字节）
 * 总长度：9字节
 * 返回：前导码（2字节）+ 目标设备编号（2字节）+ 操作类型（1字节）
 * + 寄存器内容（1字节）+ 寄存器地址（1字节）+ 校验（1字节）
 * + 包尾（2字节）
 * 总长度：10字节
 * 
 * 4.读取数据（读取数据的值）
 * 前导码（2字节） +  目标设备编号（2字节） + 操作类型（1字节）
 * + 数据长度（2字节）+ 储存器地址（2字节） + 校验（1字节）
 * + 包尾（2字节）
 * 总长度：12字节
 * 返回：前导码（2字节）+ 目标设备编号（2字节）+ 操作类型（1字节）
 * + 储存器内容（N字节）+ 储存器地址（2字节）+ 校验（1字节）
 * + 包尾（2字节）
 * 总长度：10 + N字节
 * 
 * 5.拓展指令（设置单片机属性专用）
 * 前导码（2字节）+ 目标设备编号（2字节）+ 命令（1字节）
 * + 校验（1字节）+ 包尾（2字节）
 * 总长度：8字节
 */

#include "main.h"
#include "TiantuBus.h"
#include "usart.h"
#include "stdlib.h"
#include <string.h>
//本机当前状态变量
uint8_t own_state = STATE_IDLE;
//收发缓存，所有数据都得存到收发缓存
//然后统一进行发送和读取
uint8_t TiantuBus_tx_buffer[BUFFER_SIZE];   //发送缓冲区
uint8_t TiantuBus_rx_buffer[BUFFER_SIZE];   //接收缓冲区，什么垃圾数据全部都扔这里
uint8_t TiantuBus_rx[BUFFER_SIZE];          //储存那些接收到的数据

//结构体构建函数指针
typedef struct {
    void (*sendData)(uint8_t *data, uint16_t len); //万能发送数据的函数
} CommInterface;

CommInterface *commInterface; //创建结构体，不能删除，否则会报错（内存堆栈溢出）


void uart_transmit_data(uint8_t *trBuf,uint16_t len);
/**
 * @brief TiantuBus通信协议初始化
 * 移植代码请修改这里，即将你的发送/接受的函数映射到上面
 */
void TiantuBus_Init(void)
{
	//给结构体分配内存，不能删除，否则会报错（内存堆栈溢出）
	commInterface = malloc(sizeof(CommInterface));
	//你可以修改的部分
	//在commInterface -> sendData = 右边写你要写的发送函数
    commInterface -> sendData = uart_transmit_data;
}

/**
 * @brief 和校验，将所有的数组的和加起来，得到的一个校验码的值
 * @param buf 需要校验的数组
 * @param len 数组长度
 * @return uint8_t 和
 */
uint8_t check_sum(uint8_t *buf,uint8_t len)
{
    uint8_t x = 0;
    uint8_t i = 0;
    for(i = 0; i < len; i++)
    {
        x = x + buf[i];
    }
    return x;
}

/**
 * @brief 发送任意长度的数据，在这里写你的通信方式
 * 如何发送任意长度的数据
 * 移植代码请修改这里（IIC.SPI.UART等通信协议通用）
 * @param trBuf 发送的数据
 * @param len 数据长度
 */
void uart_transmit_data(uint8_t *trBuf,uint16_t len)
{
    //限制发送长度，不得超出缓冲数组的长度，防止溢出
    if(len > BUFFER_SIZE)
    {
        len = BUFFER_SIZE;
    }
    HAL_UART_Transmit_IT(&huart1,trBuf,len);  //串口1非阻塞式发送
}

void iic_transmit_data(uint8_t *trBuf,uint16_t len)
{
    //限制发送长度，不得超出缓冲数组的长度，防止溢出
    if(len > BUFFER_SIZE)
    {
        len = BUFFER_SIZE;
    }
    //iic_send_data
}

void spi_transmit_data(uint8_t *trBuf,uint16_t len)
{
    //限制发送长度，不得超出缓冲数组的长度，防止溢出
    if(len > BUFFER_SIZE)
    {
        len = BUFFER_SIZE;
    }
    //spi_send_data
}

void xxx_transmit_data(uint8_t *trBuf,uint16_t len)
{
    //限制发送长度，不得超出缓冲数组的长度，防止溢出
    if(len > BUFFER_SIZE)
    {
        len = BUFFER_SIZE;
    }
    //xxx_send_data
}


/**
 * @brief TiantuBus发送函数
 * @param target_unit 目标器件地址（0xFFFF为广播状态，即任意器件都可以接受）
 * @param ctrl_type 操作类型
 * @param reg_addr 数据地址
 * @param trBuf 数据包内容
 * @param len 数据长度(包括读/写)
 */
// void TiantuBus_Transmit(uint16_t target_unit,uint8_t ctrl_type,
// uint16_t reg_addr,uint8_t *trBuf,uint16_t len)
// {
//     uint8_t buffer[len + 13];
//     uint8_t i = 0;
//     //包头
//     buffer[0] = Head_Code_1;
//     buffer[1] = Head_Code_2;
//     //目标器件地址
//     buffer[2] = target_unit/256;
//     buffer[3] = target_unit%256;
//     //当前状态
//     buffer[4] = own_state;
//     //操作类型
//     buffer[5] = ctrl_type;
//     //目标器件地址
//     buffer[6] = reg_addr/256;
//     buffer[7] = reg_addr%256;
//     //对读写的操作进行区分
//     if(ctrl_type == CTRL_READ_DATA)  //读
//     {
//         //读的长度下，数据长度默认为2
//         buffer[8] = 0;
//         buffer[9] = 2;
//         //需要读取的数据长度，范围1-65535
//         buffer[10] = len/256;
//         buffer[11] = len%256;
//         //和校验
//         buffer[12] = check_sum(buffer,12);
//         //包尾
//         buffer[13] = Tail_Code_1;
//         buffer[14] = Tail_Code_2;
//     }
//     else                            //写
//     {
//         //需要写的数据长度，范围1-65535
//         buffer[8] = len/256;
//         buffer[9] = len%256;
//         //将需要写的数据全部放入数据包
//         for(i=0;i<len;i++)
//         {
//             buffer[10 + i] = trBuf[i];
//         }
//         //校验和
//         buffer[10 + len] = check_sum(buffer,10 + len);
//         //包尾
//         buffer[11 + len] = Tail_Code_1;
//         buffer[12 + len] = Tail_Code_2;
//     }
//     //发送数据
//     commInterface -> sendData(buffer,len + 13);
// }

/**
 * @brief 

 */

/**
 * @brief 发送命令（控制寄存器）
 * 格式：
 * 前导码（2字节） +  目标设备编号（2字节） + 操作类型（1字节）
 * + 命令（1字节）+ 寄存器地址（1字节）+ 校验（1字节）+ 包尾（2字节）
 * 总长度：10个字节
 * 返回：应答拓展指令
 * 
 * @param target_unit  目标设备编号（2字节）
 * @param cmd          命令（1字节）     
 * @param reg_addr     寄存器地址（1字节）
 */
void TiantuBus_Transmit_Cmd(uint16_t target_unit,uint8_t cmd,uint8_t reg_addr)
{
    //包头（2字节）
    TiantuBus_tx_buffer[0] = Head_Code_1;
    TiantuBus_tx_buffer[1] = Head_Code_2;
    //目标设备编号（2字节）
    TiantuBus_tx_buffer[2] = target_unit/256;
    TiantuBus_tx_buffer[3] = target_unit%256;
    //操作类型_写命令（1字节）
    TiantuBus_tx_buffer[4] = CTRL_WRITE_CMD;
    //具体命令（1字节）
    TiantuBus_tx_buffer[5] = cmd;
    //寄存器地址（1字节）
    TiantuBus_tx_buffer[6] = reg_addr;
    //和校验（1字节）
    TiantuBus_tx_buffer[7] = check_sum(TiantuBus_tx_buffer,7);
    //包尾（2字节）
    TiantuBus_tx_buffer[8] = Tail_Code_1;
    TiantuBus_tx_buffer[9] = Tail_Code_2;
    //数据发送
    commInterface -> sendData(TiantuBus_tx_buffer,10);
}

/**
 * @brief 发送数据（设置储存变量的值）
 * 前导码（2字节） +  目标设备编号（2字节） + 操作类型（1字节）
 * + 数据长度（2字节）+ 数据包内容（N字节）+ 存储器地址（2字节）
 * + 校验（1字节）+ 包尾（2字节）
 * 总长度：12 + N个字节
 * 返回：应答拓展指令
 * 
 * @param target_unit 目标设备编号（2字节）
 * @param data_length 数据长度（2字节)
 * @param dat 
 * @param dat_addr 
 */
void TiantuBus_Transmit_Data(uint16_t target_unit,uint16_t data_length,uint8_t *dat,
uint16_t dat_addr)
{
    uint16_t n;
    //包头（2字节）
    TiantuBus_tx_buffer[0] = Head_Code_1;
    TiantuBus_tx_buffer[1] = Head_Code_2;
    //目标设备编号（2字节）
    TiantuBus_tx_buffer[2] = target_unit/256;
    TiantuBus_tx_buffer[3] = target_unit%256;
    //操作类型_写数据（1字节）
    TiantuBus_tx_buffer[4] = CTRL_WRITE_DATA;
    //数据长度（2字节）
    TiantuBus_tx_buffer[5] = data_length/256;
    TiantuBus_tx_buffer[6] = data_length%256;
    //数据包内容（N字节）
    for(n = 0;n < data_length;n++)
    {
        TiantuBus_tx_buffer[n + 7] = dat[n];
    }
    //储存器地址（2字节）
    TiantuBus_tx_buffer[data_length + 7] = dat_addr/256;
    TiantuBus_tx_buffer[data_length + 8] = dat_addr%256;
    //和校验（1字节）
    TiantuBus_tx_buffer[data_length + 9] = check_sum(TiantuBus_tx_buffer,data_length + 9);
    //包尾（2字节）
    TiantuBus_tx_buffer[data_length + 10] = Tail_Code_1;
    TiantuBus_tx_buffer[data_length + 11] = Tail_Code_2;
    //数据发送
    commInterface -> sendData(TiantuBus_tx_buffer,data_length + 12);
}

/**
 * @brief 拓展指令（设置单片机属性专用）
 * 前导码（2字节）+ 目标设备编号（2字节）+ 操作类型（1字节）+ 拓展命令（1字节）
 * + 校验（1字节）+ 包尾（2字节）
 * 总长度：9字节
 * 
 * @param target_unit 目标设备编号（2字节）
 * @param expand_cmd 拓展命令（1字节）
 */
void TiantuBus_Transmit_Expand(uint16_t target_unit,uint8_t expand_cmd)
{
    //包头（2字节）
    TiantuBus_tx_buffer[0] = Head_Code_1;
    TiantuBus_tx_buffer[1] = Head_Code_2;
    //目标设备编号（2字节）
    TiantuBus_tx_buffer[2] = target_unit/256;
    TiantuBus_tx_buffer[3] = target_unit%256;
    //操作字节（拓展指令）
    TiantuBus_tx_buffer[4] = CTRL_EXPAND;
    //拓展命令（1字节）
    TiantuBus_tx_buffer[5] = expand_cmd;
    //校验
    TiantuBus_tx_buffer[6] = check_sum(TiantuBus_tx_buffer,6);
    //包尾（2字节）
    TiantuBus_tx_buffer[7] = Tail_Code_1;
    TiantuBus_tx_buffer[8] = Tail_Code_2;
    //数据发送
    commInterface -> sendData(TiantuBus_tx_buffer,9);
}

//添加一个数据到缓冲数组里面！
uint16_t now_length = 0;//当前已经接收到了的函数

//用于将一个数据放入里面
//代码移植，只需要将每接收到一个数据的时候
//就把接收到的数据push进入TiantuBus_rx_buffer即可
void add_to_recv_buf(uint8_t dat)
{
    if(now_length == BUFFER_SIZE)  //接受的数据溢出了，清空，重新开始接收
    {
        memset(TiantuBus_rx_buffer,0x00,BUFFER_SIZE);
        now_length = 0;
        return;
    }
    TiantuBus_rx_buffer[now_length] = dat;
    now_length ++;
}

//数据处理
//每1ms循环检测一次里面的数据
//如果数据满足要求，返回1，并且根据数据进行操作
//否则无论是否处理完毕，都要返回0
//当检测到包尾时，开始进行数据的判断
uint8_t buffer_handle(void)
{
    //判断是否接受完毕一次数据
    //没有任何一条指令的长度是小于9的，如果是的话，请自动退出
    if(now_length < 9)
    {
        return 0;
    }
    //判断包尾是否正确，不正确则退出，正确则继续判断
    if (TiantuBus_rx_buffer[now_length - 1] != Tail_Code_2 ||
        TiantuBus_rx_buffer[now_length - 2] != Tail_Code_1)
    {
        return 0;  //包尾没有检测到
    }
    //检查包头是否正确
    if(TiantuBus_rx_buffer[0] != Head_Code_1 ||
        TiantuBus_rx_buffer[1] != Head_Code_2)
    {
        memset(TiantuBus_rx_buffer,0x00,BUFFER_SIZE);
        now_length = 0;
        return 0; //包头不正确，退出，清空数据
    }
    //检查编号是否正确
    if(TiantuBus_rx_buffer[2] != Unit_Number/256 ||
        TiantuBus_rx_buffer[3] != Unit_Number%256)
    {
        memset(TiantuBus_rx_buffer,0x00,BUFFER_SIZE);
        now_length = 0;
        return 0; //编号不正确，退出，清空数据
    }
    //检查校验是否正确
    if(TiantuBus_rx_buffer[now_length - 3] != check_sum(TiantuBus_rx_buffer,now_length - 3))
    {
        memset(TiantuBus_rx_buffer,0x00,BUFFER_SIZE);
        now_length = 0;
        return 0; //编号不正确，退出，清空数据
    }
    //正确，接下来判断类型
    return 1;
}
/**
 * @brief TiantuBus 1ms 的定时任务 用于处理数据 等待应答等
 * @return uint8_t 
 */
uint8_t TiantuBus_1ms_task(void)
{
    if(buffer_handle() == 1)
    {
        HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		memset(TiantuBus_rx_buffer,0x00,BUFFER_SIZE);
        now_length = 0;
    }
	return 0;
}
